/*
 * Unpacker.java
 *
 * Created on July 4, 2006, 11:30 AM
 *
 */

package galronnlp.pcfg.unpacker;

import galronnlp.pcfg.parser.*;

import galronnlp.util.BinaryTree;
import galronnlp.util.UnaryTree;
import galronnlp.util.LexicalTree;
import galronnlp.util.Tree;
import galronnlp.util.Generator;
import galronnlp.util.SortedLinkedList;
import galronnlp.util.Tuple;
import galronnlp.util.Symbol;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * An unpacker which mimics the python generator. I highly advise against using this
 * without getting finer control over Java thread scheduling!
 *
 * @author Daniel A. Galron
 */
public class Unpacker extends Generator {
    
    Chart chart;
    int begin;
    int end;
    Symbol cat;
    
    /** Creates a new instance of Unpacker */
    public Unpacker(Chart c, int begin, int end, Symbol cat) {
        this.results = new LinkedList();
        this.chart = c;
        this.begin = begin;
        this.end = end;
        this.cat = cat;
        this.start();
    }
    
    /**
     * Generates parses and puts them in a SortedLinkedList, ordered by the weights
     * of the trees.
     */
    public synchronized void generate() {
        ChartEntry topChartEntry = chart.get(this.begin, this.end, this.cat);
        SortedLinkedList items = topChartEntry.items();
        // Iterate through all productions spanning (begin, end)
        for(Iterator<EntryTriple> i = items.iterator(); i.hasNext(); ) {        
            EntryTriple et = i.next();
            double p = ((Double)et.get(0)).doubleValue();
            Edge edge = (Edge)et.get(2);
            if(edge.type() == Edge.UNARY) {
                // Create a new unpacker for the child of the edge spanning (begin, end)
                // The unpacker will automatically start unpacking the subtree.
                Unpacker unpack = new Unpacker(this.chart, this.begin, this.end, (Symbol)edge.entry());
                // We need to put this thread to sleep, so that the unpacker for the
                // subtree has time to put at least one parse in its parse list.
                // Ideally, I would simply suspend this thread until a result has
                // been released by the unpacker for the subtree.
                try {
                    sleep(1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                // While the unpacker for the child is still running, or while there
                // is subtree in the result list, construct a new Unary tree with the
                // result as a subtree
                while(unpack.running || unpack.hasNext()) {
                    if(unpack.hasNext()) {
                        Tree RHS = (Tree)unpack.getNext();
                        Tree tree = new UnaryTree(this.cat, RHS, p);
                        synchronized(this.results) {
                            this.results.addLast(tree);
                        }
                    }
                }
            } else if(edge.type() == Edge.BINARY) {
                // Create a new unpacker for the children of the edge spanning (begin, end)
                // The unpacker will automatically start unpacking the subtree.
                int mid = ((Integer)((Tuple)edge.entry()).get(0)).intValue();
                Symbol lRHS = (Symbol)((Tuple)edge.entry()).get(1);
                Symbol rRHS = (Symbol)((Tuple)edge.entry()).get(2);
                Unpacker l_unpack;
                Unpacker r_unpack;
                l_unpack = new Unpacker(this.chart, this.begin, mid, lRHS);
                r_unpack = new Unpacker(this.chart, mid, this.end, rRHS);
                    try {
                        sleep(1000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                while(l_unpack.running || l_unpack.hasNext()) {
                    if(l_unpack.hasNext()) {
                        Tree l_RHS = (Tree)l_unpack.getNext();
                        while(r_unpack.running || r_unpack.hasNext()) {
                            if(r_unpack.hasNext()) {
                                Tree r_RHS = (Tree)r_unpack.getNext();
                                Tree tree = new BinaryTree(this.cat, l_RHS, r_RHS, p);
                                synchronized(this.results) {
                                    this.results.addLast(tree);
                                }
                            }
                        }
                    }
                }
            } else if(edge.type() == Edge.LEXICAL) {
                LexicalTree lt = new LexicalTree(this.cat, (Symbol)edge.entry(), p);
                synchronized(this.results) {
                    this.results.addLast(lt);
                }
            } else {
                System.out.println("I shouldn't be here!");
            }
        }
        notifyAll();
    }
}
